﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;

namespace moose.irc {
    /// <summary>
    /// IRC Channel
    /// </summary>
    [CLSCompliant(true)]
    public class IrcChannel : IrcEntity {

        #region [Local Attributes]

        /// <summary>
        /// Channel Topic
        /// </summary>
        private string m_topic;

        /// <summary>
        /// Channel Modes
        /// </summary>
        private List<IrcChannelMode> m_channel_modes;

        /// <summary>
        /// Users in channel
        /// </summary>
        private IrcUsersList m_users;

        #endregion

        #region [Public Properties]

        /// <summary>
        /// Gets/Sets Channel Topic
        /// </summary>
        public string Topic {
            get {
                return m_topic;
            }
            set {
                m_topic = value;
                return;
            }
        }

        #endregion

        #region [Construction]

        /// <summary>
        /// Initialize IRC Channel
        /// </summary>
        /// <param name="name">Name of channel</param>
        public IrcChannel(string name)
            : base(name) {
            m_channel_modes = new List<IrcChannelMode>();
            m_users = new IrcUsersList();

            return;
        }

        #endregion

        /// <summary>
        /// Just resets the channel, as we just joined
        /// </summary>
        public void reset() {
            m_channel_modes.Clear();
            m_users.clear();
            Topic = string.Empty;
            return;
        }

        #region [Channel Modes]

        /// <summary>
        /// Returns Channel Modes
        /// </summary>
        /// <returns>IEnumerable Collection of IrcChannelModes</returns>
        public IEnumerable<IrcChannelMode> get_modes() {
            return m_channel_modes;
        }

        /// <summary>
        /// Parses Mode String, and updates channel modes accordingly
        /// </summary>
        /// <param name="mode_string">mode string to parse, and use to update channel modes</param>
        public void update_modes(string mode_string) {
            IrcChannelMode mode = null;
            bool add = false, kill = false;
            char c = (char)0x0;
            mode_string = mode_string.Trim();

            for (int i = 0; i < mode_string.Length && !kill; i++) {
                c = mode_string[i];
                if (c == '+') { add = true; continue; } 
                else if (c == '-') { add = false; continue; }

                //TODO: optimize this a little, alot of duplicated conditions... but no rush.. this works...
                
                var channel_modes = from cm in m_channel_modes where cm.Mode == c select cm;
                if (add) { //we're adding
                    if (channel_modes.Count() > 0) { //we already have this mode
                        mode = channel_modes.First(); //so, lets work with that
                    } else { //we do not have this mode
                        mode = new IrcChannelMode(c); //create a new one
                        m_channel_modes.Add(mode); //add it as a new channel mode
                    }

                    if (i < mode_string.Length - 1) { //Test if there are any arguments for this item
                        if (mode_string[i + 1] == ' ') { //There is an argument
                            i++;
                            for (; i < mode_string.Length; i++) {
                                c = mode_string[i];
                                if (c == ' ') continue;
                                else if (c == '-') { add = false; break; } 
                                else if (c == '+') { add = true; break; } 
                                else if (c == '*') { mode.clear_arguments(); break; } 
                                else if (c == ':') {
                                    mode.add_arg(mode_string.Substring(i).Replace(':', ' ').Trim());
                                    i = mode_string.Length;
                                    break;
                                } else {
                                    string arg = null;
                                    int arg_end = mode_string.Substring(i).IndexOf(' ');
                                    if (arg_end == -1)
                                        arg = mode_string.Substring(i);
                                    else
                                        arg = mode_string.Substring(i, arg_end);

                                    mode.add_arg(arg);
                                    kill = true;
                                    break;
                                }
                            }
                        }
                    }
                } else { //we're removing
                    if (channel_modes.Count() == 0) continue;
                    mode = channel_modes.First();
                    if (i < mode_string.Length - 1) {
                        if (mode_string[i + 1] == ' ') { //There is an argument
                            i++;
                            for (; i < mode_string.Length; i++) {
                                c = mode_string[i];
                                if (c == ' ') continue;
                                else if (c == '-') { add = false; break; } 
                                else if (c == '+') { add = true; break; } 
                                else if (c == '*') { m_channel_modes.Remove(mode); break; } 
                                else if (c == ':') {
                                    mode.remove_arg(mode_string.Substring(i).Replace(':', ' ').Trim());
                                    i = mode_string.Length;
                                    break;
                                } else {

                                    string arg = null;
                                    int arg_end = mode_string.Substring(i).IndexOf(' ');
                                    if (arg_end == -1)
                                        arg = mode_string.Substring(i);
                                    else 
                                        arg = mode_string.Substring(i, arg_end);
                                    
                                    mode.remove_arg(arg);
                                    kill = true;
                                    break;
                                }
                            }
                        } else {
                            m_channel_modes.Remove(mode);
                        }
                    } else {
                        m_channel_modes.Remove(mode);
                    }
                } 
            }
            return;
        }

        #endregion

        #region [Users]

        /// <summary>
        /// Adds a user to the channel
        /// </summary>
        /// <param name="user">User to add</param>
        public void add_user(IrcUser user) {
            m_users.get(user);
            return;
        }

        /// <summary>
        /// Removes a user from the channel
        /// </summary>
        /// <param name="user">User to remove</param>
        public void remove_user(IrcUser user) {
            m_users.remove(user);
            return;
        }

        /// <summary>
        /// Return reference to all users
        /// </summary>
        /// <returns>collection of all users</returns>
        public IEnumerable<IrcUser> get_users() {
            return m_users.get();
        }

        #endregion

    }
}
